home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gigarom 1
/
Gigarom Macintosh Archives (Quantum Leap)(CDRM1080320)(1993).iso
/
FILES
/
APP
/
A-D
/
BBEdit 2.2.2.sea
/
BBEdit 2.2.2
/
BBEdit Extensions
/
Sources
/
827.c
next >
Wrap
C/C++ Source or Header
|
1992-10-19
|
20KB
|
605 lines
/*
* 827.c
*
* By Jamie McCarthy, Sept 92. This is public domain.
* If you have any questions or comments, you can reach
* me at "k044477@kzoo.edu" on the Internet, or at
* "j.mccarthy" on AppleLink.
*
* This code module isn't terribly exciting, but it does
* provide a little more sample code, including doing
* a callback from assembly language (whoopee!).
*
* I take responsibility for the messy code, but the
* ugly symbol names with underscores in them were
* Rich's idea. ;-)
*
*/
/******************************/
#include <SetupA4.h>
#include "ExternalInterface.h"
#include "DialogUtilities.h"
/******************************/
/*
* The DITL item numbers for the various controls.
*/
enum {
k_essetToB = 3,
k_essetToSS,
k_euroquotesToQuotes,
k_euroquotesToInequalities,
k_bulletToStar,
k_bulletToO,
k_convertSelection,
k_convertDocument,
k_changeInPlace,
k_copyToClipboard,
k_line
} ;
/******************************/
/*
* The preferences.
*/
static struct
{
Boolean essetToSS;
Boolean euroquotesToInequalities;
Boolean bulletToO;
Boolean convertDocument;
Boolean copyToClipboard;
} options8To7;
/*
* Was something selected when we started?
*/
static Boolean nonEmptySelection;
/*
* These strings are only for chars with their hi bit set. Mask off
* the hi bit, index into this array, and you have a _backwards_ C
* string, max length 4, for what to replace. (They've got a max
* length of 4 because they have to all be the same length, and you
* can't have initialized strings in code segments, only char
* constants. See the ThC5 User Manual, p. 496. And they're backwards
* because the algorithm's fastest that way. :-) For length-4
* strings, just put them in the char constant. For shorter strings,
* stick a terminating null on the end and put whatever you like in
* any of the remaining bytes. Note that if you write, say, 'a\0',
* it will be interpreted as a 16-bit integer, and will be stored as
* if you'd written '\0\0a\0', which is not what you want.
*
* Do not substitute a length-0 string for any char. The algorithm
* doesn't support this. Sorry. Email me with a good reason for
* mapping a character to nothing and I'll think about changing it.
*/
static unsigned long replacement[128] = {
/* 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 */
/* 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F */
'eA\0x', 'A\0xx', 'C\0xx', 'E\0xx', 'N\0xx', 'eO\0x', 'eU\0x', 'a\0xx', // 0x80
'a\0xx', 'a\0xx', 'ea\0x', 'a\0xx', 'a\0xx', 'c\0xx', 'e\0xx', 'e\0xx', // 0x88
'e\0xx', 'e\0xx', 'i\0xx', 'i\0xx', 'i\0xx', 'i\0xx', 'n\0xx', 'o\0xx', // 0x90
'o\0xx', 'o\0xx', 'eo\0x', 'o\0xx', 'u\0xx', 'u\0xx', 'u\0xx', 'eu\0x', // 0x98
'*\0xx', '*\0xx', 'c\0xx', '#\0xx', 'S\0xx', 'xxx\0', 'P\0xx', 'xxx\0', // 0xA0
')R(\0', ')c(\0', ')MT(', '\'\0xx',' \0xx', '><\0x', 'EA\0x', 'O\0xx', // 0xA8
')()(', '-/+\0', '=<\0x', '=>\0x', 'neY\0', 'u\0xx', 'd\0xx', 'muS\0', // 0xB0
'IP\0x', 'ip\0x', 'tnI\0', 'a\0xx', 'o\0xx', 'mhO\0', 'ea\0x', 'o\0xx', // 0xB8
'?\0xx', '!\0xx', '-\0xx', '/\0xx', 'f\0xx', '=\0xx', 'D\0xx', 'xxx\0', // 0xC0
'xxx\0', '...\0', ' \0xx', 'A\0xx', 'A\0xx', 'O\0xx', 'EO\0x', 'eo\0x', // 0xC8
'-\0xx', '--\0x', '"\0xx', '"\0xx', '\'\0xx','\'\0xx','/\0xx', 'o\0xx', // 0xD0
'ey\0x', 'eY\0x', '/\0xx', 'O\0xx', '<\0xx', '>\0xx', 'if\0x', 'lf\0x', // 0xD8
'*\0xx', '.\0xx', ',\0xx', ',\0xx', '00/0', 'A\0xx', 'E\0xx', 'A\0xx', // 0xE0
'eE\0x', 'E\0xx', 'I\0xx', 'I\0xx', 'I\0xx', 'I\0xx', 'O\0xx', 'O\0xx', // 0xE8
'*\0xx', 'O\0xx', 'U\0xx', 'U\0xx', 'U\0xx', 'i\0xx', '^\0xx', '~\0xx', // 0xF0
'\'\0xx','\'\0xx','\'\0xx','*\0xx', ',\0xx', '"\0xx', ',\0xx', '\'\0xx' // 0xF8
} ;
/*
* This array counts how much longer the replacement is than
* what's it's replacing.
*/
static char repExtraLength[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 3, 0, 0, 1, 1, 0,
3, 2, 1, 1, 2, 0, 0, 2, 1, 1, 2, 0, 0, 2, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 1,
0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1,
0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
} ;
/******************************/
/*
* How often should the progress thermometer be updated?
*/
#define nPrgsBytes (16384L)
/******************************/
void maintain_buttons(DialogPtr d);
void updateReplacementArray(void);
long resizeTextHndl(ExternalCallbackBlock *callbacks,
Handle text, long offset, long changeStart, long changeEnd,
long *selStart, long *selEnd);
void replaceText(ExternalCallbackBlock *callbacks,
Handle text, long offset, long changeStart, long changeEnd,
long textLen, long lengthIncrease);
pascal void main(ExternalCallbackBlock *callbacks, WindowPtr w);
/******************************/
void maintain_buttons(DialogPtr d)
{
if (!nonEmptySelection) {
XAbleDlgCtl(d, k_convertSelection, FALSE);
options8To7.convertDocument = TRUE;
}
SetDlgCtl(d, k_essetToB, !options8To7.essetToSS);
SetDlgCtl(d, k_essetToSS, options8To7.essetToSS);
SetDlgCtl(d, k_bulletToStar, !options8To7.bulletToO);
SetDlgCtl(d, k_bulletToO, options8To7.bulletToO);
SetDlgCtl(d, k_euroquotesToQuotes, !options8To7.euroquotesToInequalities);
SetDlgCtl(d, k_euroquotesToInequalities, options8To7.euroquotesToInequalities);
SetDlgCtl(d, k_convertSelection, !options8To7.convertDocument);
SetDlgCtl(d, k_convertDocument, options8To7.convertDocument);
SetDlgCtl(d, k_changeInPlace, !options8To7.copyToClipboard);
SetDlgCtl(d, k_copyToClipboard, options8To7.copyToClipboard);
}
void updateReplacementArray(void)
{
/*
* Apparently, there's this nasty restriction about assigning
* to global arrays in a code segment. I'm not sure why that is,
* and I don't know if I'm gonna break something by doing this.
*
* But hey, let's fool the compiler. It's fun and easy! We'll
* take the address where we'd like to stuff a value, cast its
* type to a regular old (unsigned long *), and happily write
* to that location.
*/
if (options8To7.essetToSS) {
*(unsigned long*)(&replacement[0x00A7 - 128]) = 'ss\0\0';
*(char *)(&repExtraLength[0x00A7]) = 1;
} else {
*(unsigned long*)(&replacement[0x00A7 - 128]) = 'B\0\0\0';
*(char *)(&repExtraLength[0x00A7]) = 0;
}
if (options8To7.euroquotesToInequalities) {
*(unsigned long*)(&replacement[0x00C7 - 128]) = '<<\0\0';
*(unsigned long*)(&replacement[0x00C8 - 128]) = '>>\0\0';
*(char *)(&repExtraLength[0x00C7]) = 1;
*(char *)(&repExtraLength[0x00C8]) = 1;
} else {
*(unsigned long*)(&replacement[0x00C7 - 128]) = '"\0\0\0';
*(unsigned long*)(&replacement[0x00C8 - 128]) = '"\0\0\0';
*(char *)(&repExtraLength[0x00C7]) = 0;
*(char *)(&repExtraLength[0x00C8]) = 0;
}
if (options8To7.bulletToO) {
*(unsigned long*)(&replacement[0x00A5 - 128]) = 'o\0\0\0';
*(char *)(&repExtraLength[0x00A5]) = 0;
} else {
*(unsigned long*)(&replacement[0x00A5 - 128]) = '*\0\0\0';
*(char *)(&repExtraLength[0x00A5]) = 0;
}
}
/******************************/
/*
* In these routines, the serious ones where work actually gets done,
* we want to call the DoProgress() callback. It's nice to give
* feedback, just in case this is going to take a long time. (If
* you haven't seen the progress-o-meter, try converting a lot of
* text: less than about two megs, and the dialog doesn't even
* show up on my IIci.)
*
* But we don't want to call DoProgress() after processing each
* character; we'd spend lots more time doing that than actually
* doing any work. But neither would it make sense to call the
* callback after the loop was over, eh? So we strike a compromise:
* we decide on a certain number of bytes, and only call
* DoProgress() every time we reach that number of bytes. (It's
* predefined as 16K.)
*
* One way of doing this would be to loop the pointer until it
* reaches its destination, and meanwhile increment a counter
* until it reached the value that means "call DoProgress()."
* But that's a little wasteful; there's no sense keeping track
* of two counters when one will do. So, both resizeTextHndl()
* and replaceText() use a slightly more efficient approach.
*
* They set a "check pointer" to the initial pointer, and add in
* that 16K. Then, they make sure it's not past the _end_ of
* where they're supposed to be going. If it is, they set it to
* the end. Then they do the loop, and the only test that's made
* is against the check pointer. When the index pointer equals
* the check pointer, if it's equal to the end pointer, we're
* done! If not, call DoProgress(), add another 16K to the check
* pointer, make sure it's not past the end pointer, and keep
* looping. (The flow isn't _exactly_ the same in the assembler
* and C versions, but they both do the same thing.)
*/
long resizeTextHndl(ExternalCallbackBlock *callbacks,
Handle text, long offset, long changeStart, long changeEnd,
long *selStart, long *selEnd)
/*
* Walk through the text, figuring out how many chars we'll
* need to extend the length by. Then resize the handle,
* and return the number of chars that it was extended.
*/
{
long oldLength;
register long extraLength;
register char nExtraChars;
long actualLength;
register unsigned char *srcP;
register unsigned char *checkP;
unsigned char *selStartP, *selEndP, *changeEndP;
oldLength = GetHandleSize(text);
srcP = (unsigned char *) *text - offset + changeStart;
selStartP = (unsigned char *) *text - offset + *selStart;
selEndP = (unsigned char *) *text - offset + *selEnd;
changeEndP = (unsigned char *) *text - offset + changeEnd;
extraLength = 0;
checkP = srcP + nPrgsBytes;
if (checkP > selStartP) checkP = selStartP;
/*
* Walk through up to the start of the selection. While we're
* walking here, selStart and selEnd get increased for each
* extra char we'll be adding.
*/
while (srcP < selStartP) {
while (srcP < checkP) {
nExtraChars = repExtraLength[*srcP++];
extraLength += nExtraChars;
if (nExtraChars > 0) {
*selStart += nExtraChars;
*selEnd += nExtraChars;
}
}
/*
* Assume this routine takes half the total time, and that
* replaceText() takes the other half.
*/
callbacks->DoProgress( (srcP - ((unsigned char*) *text - offset + changeStart)) / 2 );
checkP += nPrgsBytes;
if (checkP > selStartP) checkP = selStartP;
}
/*
* Walk through, between the start and the end of the selection.
* While walking here, only selEnd gets bumped up.
*/
while (srcP < selEndP) {
while (srcP < checkP) {
nExtraChars = repExtraLength[*srcP++];
extraLength += nExtraChars;
if (nExtraChars > 0) {
*selEnd += nExtraChars;
}
}
callbacks->DoProgress( (srcP - ((unsigned char*) *text - offset + changeStart)) / 2 );
checkP += nPrgsBytes;
if (checkP > selEndP) checkP = selEndP;
}
/*
* Walk after the selection. In this part of the document,
* neither selStart nor selEnd need be pushed back.
*/
while (srcP < changeEndP) {
while (srcP < checkP) {
extraLength += repExtraLength[*srcP++];
}
callbacks->DoProgress( (srcP - ((unsigned char*) *text - offset + changeStart)) / 2 );
checkP += nPrgsBytes;
if (checkP > changeEndP) checkP = changeEndP;
}
SetHandleSize(text, oldLength + extraLength);
actualLength = GetHandleSize(text);
if (actualLength != oldLength + extraLength) return -1;
else return extraLength;
}
void replaceText(ExternalCallbackBlock *callbacks,
Handle text, long offset, long changeStart, long changeEnd,
long textLen, long lengthIncrease)
/*
* The main loop: run through and replace everything between
* changeStart and changeEnd. Since the new text will be either
* the same size or longer, we start at the _end_ and run to the
* _beginning_.
*/
{
register unsigned char sc; // The source character: the 8-bit character
// presently being converted.
register long count;
unsigned char *finalSP;
unsigned char *initialSP;
/*
* First, take everything past changeEnd and bump it up where
* it's supposed to go.
*/
BlockMove(*text - offset + changeEnd,
*text - offset + changeEnd + lengthIncrease,
textLen - offset - changeEnd);
/*
* In code segments, ThC won't let you have more than two address
* registers, even if you ask politely with sugar on top. So
* I'm not going to bother asking, I'm just going to take them.
*/
#define checkP a0 // When srcP gets here, check to see if we're done.
#define scratchPtr a0 // A scratch pointer.
#define replPtr a1 // The pointer into the replacement array.
#define doProgPtr a1 // The pointer to the "DoProgress" callback.
#define srcP a2 // The source pointer: this points to the 8-bit
// characters that get converted.
#define destP a3 // The destination pointer: this points to the
// 7-bit characters that have been converted.
asm {
movem.l a2-a3,-(a7) // save the two address regs we're not supposed to have!
move.l (text), scratchPtr // set up...
move.l (scratchPtr), finalSP // ...
move.l offset, count // ...
sub.l count, finalSP // ...
move.l finalSP, srcP // ...
add.l changeEnd, srcP // ...srcP...
move.l srcP, destP // ...
move.l srcP, initialSP // ...initialSP...
add.l lengthIncrease, destP // ...destP...
move.l destP, checkP // ...checkP...
move.l changeStart, count // ...
add.l count, finalSP // ...and finalSP
clr.l sc // clear the source char reg
bra.s @updateCheckP // make sure we won't go too far
@loop: move.b -(srcP), sc // load the source char reg
bmi.s @char8 // if hi bit set, it's an 8-bit char
// otherwise it's a normal 7-bit char
@char7: move.b sc, -(destP) // just move it to the destination
cmpa.l srcP, checkP // do we need to do a checkup?
bne.s @loop // if not, continue
bra.s @doCheckup // if so, do it
@char8: lea replacement, replPtr // put the addr of the array into replPtr
andi.b #0x7F, sc // mask off the hi bit of the 8-bit char
add.b sc, sc // index 4*sc bytes into...
adda.w sc, replPtr // ...the array, since each entry...
adda.w sc, replPtr // ...is four bytes long
moveq #3, count // put a maximum of four bytes at destP
@movRep: move.b (replPtr)+, -(destP) // shove one byte
tst.b (replPtr) // is it a null char?
dbeq count, @movRep // if so, or if we've done four bytes, stop
@test: cmpa.l srcP, checkP // do we need to do a checkup?
bne.s @loop // if not, continue
// if so, do it
@doCheckup:
cmpa.l finalSP, checkP // are we totally done?
beq.s @done // yup, quit
@showProgress: // nope, update progress thermometer
movem.l d0-d1/a0-a1, -(a7) // save registers we'll need later
move.l callbacks, doProgPtr // load the routine's address...
movea.l OFFSET(ExternalCallbackBlock,DoProgress)(doProgPtr), doProgPtr
move.l initialSP, count // Calculate how far we've gone, assuming
add.l count, count // that resizeTextHndl() already took half
sub.l srcP, count // the time. (initialSP-finalSP) is the
sub.l finalSP, count // total distance, so ((2*initialSP) -
lsr.l #1, count // finalSP - srcP)/2 is our progress.
clr.w -(a7) // clear space for Boolean return
move.l count, -(a7) // push "done" parameter onto stack
jsr (doProgPtr) // do the callback!
addq #2, a7 // ignore the result
movem.l (a7)+, d0-d1/a0-a1 // restore those registers
@updateCheckP:
sub.l #nPrgsBytes, checkP // move checkP to the next checkup point
cmpa.l finalSP, checkP // is this the last leg of our journey?
bge.s @test // if not, continue
movea.l finalSP, checkP // if so, make sure we don't go too far...
bra.s @test // ...then continue
@done: movem.l (a7)+,a2-a3 // restore those registers we "stole"
}
}
/******************************/
pascal void main(ExternalCallbackBlock *callbacks, WindowPtr w)
{
DialogPtr d;
short item;
GrafPtr save_port;
long selStart, selEnd, firstChar;
short act_len;
OSErr err;
RememberA0();
SetUpA4();
callbacks->GetPreference('827 ', sizeof(options8To7), &options8To7, &act_len);
if (act_len < 0)
{
options8To7.essetToSS = FALSE;
options8To7.euroquotesToInequalities = FALSE;
options8To7.convertDocument = FALSE;
options8To7.copyToClipboard = FALSE;
};
GetPort(&save_port);
d = callbacks->CenterDialog(128);
SetPort(d);
SetupUserItem(d, k_line, callbacks->FrameDialogItem);
callbacks->GetSelection(&selStart, &selEnd, &firstChar);
nonEmptySelection = (selEnd > selStart);
do
{
maintain_buttons(d);
ModalDialog(callbacks->StandardFilter, &item);
switch (item)
{
case k_essetToB: options8To7.essetToSS = FALSE; break;
case k_essetToSS: options8To7.essetToSS = TRUE; break;
case k_euroquotesToQuotes: options8To7.euroquotesToInequalities = FALSE; break;
case k_euroquotesToInequalities: options8To7.euroquotesToInequalities = TRUE; break;
case k_bulletToStar: options8To7.bulletToO = FALSE; break;
case k_bulletToO: options8To7.bulletToO = TRUE; break;
case k_convertSelection: options8To7.convertDocument = FALSE; break;
case k_convertDocument: options8To7.convertDocument = TRUE; break;
case k_changeInPlace: options8To7.copyToClipboard = FALSE; break;
case k_copyToClipboard: options8To7.copyToClipboard = TRUE; break;
}
} while ((item != ok) && (item != cancel));
DisposDialog(d);
SetPort(save_port);
if (item == ok)
{
Handle h, text;
long textLen;
long lengthIncrease;
long changeStart, changeEnd;
long offset;
long extendLengthBy;
callbacks->SetPreference('827 ', sizeof(options8To7), &options8To7, &act_len);
h = callbacks->GetWindowContents(w);
textLen = GetHandleSize(h);
if (options8To7.convertDocument) {
changeStart = 0; changeEnd = textLen;
} else {
changeStart = selStart; changeEnd = selEnd;
}
if (options8To7.copyToClipboard) {
text = callbacks->Allocate(changeEnd-changeStart, FALSE);
if (text == NULL) {
callbacks->ReportOSError(memFullErr);
goto done8To7;
}
BlockMove(*h + changeStart, *text, changeEnd-changeStart);
offset = changeStart;
} else {
text = h;
offset = 0;
}
updateReplacementArray();
callbacks->StartProgress("\pConverting…", // string to display
changeEnd-changeStart, // total distance to go
FALSE); // can't cancel
lengthIncrease = resizeTextHndl(callbacks,
text, offset, changeStart, changeEnd,
&selStart, &selEnd);
if (lengthIncrease < 0) {
callbacks->DoneProgress();
callbacks->ReportOSError(memFullErr);
goto done8To7;
}
/*
* Here's where all the real work gets done!
*/
replaceText(callbacks,
text, offset, changeStart, changeEnd,
textLen, lengthIncrease);
callbacks->DoneProgress();
if (options8To7.copyToClipboard) {
HLock(text);
ZeroScrap();
PutScrap(changeEnd - changeStart + lengthIncrease, 'TEXT', *text);
DisposHandle(text);
} else {
callbacks->ContentsChanged(w);
callbacks->SetSelection(selStart, selEnd, firstChar);
}
}
done8To7:
RestoreA4();
}